<?php

declare(strict_types=1);

namespace App\Services;

use App\Entity\BusyPeriodEntity;
use App\Inc\OrderInc;
use App\Models\CityBusyPeriod as CityBusyPeriodModel;
use App\Models\CitySettings as CitySettingsModel;
use App\Models\SystemSettings as SystemSettingsModel;
use App\Utils\Tool;
use Illuminate\Support\Facades\DB;

class CitySettingsService
{
    public static $colsMap = [
        'cityId'              => 'city_id',
        'enabled'             => 'enabled',
        'serviceHourStart'    => 'service_hour_start',
        'serviceHourEnd'      => 'service_hour_end',
        'serviceInterval'     => 'service_interval',
        'serviceWeekdays'     => 'service_weekdays',
        'reservationHour'     => 'reservation_hour',
        'phoneNumber'         => 'phone_number',
        'showPhone'           => 'show_phone',
        'reservationDayLimit' => 'reservation_day_limit',
    ];

    public static $sysDefaultColsMap = [
        'cityId'           => 'city_id',
        'enabled'          => 'enabled',
        'serviceHourStart' => 'service_hour_start',
        'serviceHourEnd'   => 'service_hour_end',
        'serviceInterval'  => 'service_interval',
        'serviceWeekdays'  => 'service_weekdays',
        'reservationHour'  => 'reservation_hour',
        'phoneNumber'      => 'phone_number',
        'showPhone'        => 'show_phone',
    ];

    public static $colsMap2 = [
        // 'cityId' => 'city_id',
        'enabled'             => 'enabled',
        'serviceHourStart'    => 'service_hour_start',
        'serviceHourEnd'      => 'service_hour_end',
        'serviceInterval'     => 'service_interval',
        'serviceWeekdays'     => 'service_weekdays',
        'reservationHour'     => 'reservation_hour',
        'reservationDayLimit' => 'reservation_day_limit',
    ];

    // 不要删除
    public function __construct()
    {
        $this->systemSettingsM = new SystemSettingsModel();
        $this->citySettingsM   = new citySettingsModel();
    }

    //获取省市配置信息
    public function getCitySettings(int $cityId, bool $tryDefaults = false, bool $withEnabled = false)
    {
        $info = (new LocationService())->getCityById($cityId);
        if ($info) {
            $info = $info->toArray();
        }
        if ($withEnabled) {
            $settings = $this->citySettingsM->getEnabledSettings($cityId);
        } else {
            $settings = $this->citySettingsM->getSettings($cityId);
        }
        if (!$settings && $tryDefaults) {
            $settings = $this->citySettingsM->getDefaultSettings($cityId);
        }
        if ($settings) {
            $settings['service_weekdays'] = Tool::weekdaysBin2Dec($settings['service_weekdays']);
        }
        $settings['busyPeriods'] = self::getBusyPeriods($cityId);

        return [$info, $settings];

        //return compact('info', 'settings');
    }

    //保存各省市信息
    public function saveCitySettings(array $options, int $cityId)
    {
        DB::beginTransaction();
        $data = [];
        foreach (self::$colsMap as $configName => $columnName) {
            if (!isset($options[$configName])) {
                continue;
            }
            $optionValue = $options[$configName];
            if (in_array($configName, ['serviceHourStart', 'serviceHourEnd'])) {//小时字符串转浮点数
                $optionValue = $this->citySettingsM->hour2float($optionValue);
            }
            if ($configName == 'serviceWeekdays' && $optionValue) {//星期转为数字
                $weekDayArr = array_map(function ($item) {
                    return intval(trim($item));
                }, (array) explode(',', $optionValue));
                $optionValue = Tool::weekdaysDec2Bin($weekDayArr);
            }
            if (in_array($configName, ['serviceInterval', 'reservationHour'])) {//判断0.5倍数
                if ((($optionValue * 10) % 5) != 0) {
                    continue;
                }
            }
            $data[$columnName] = $optionValue;
        }
        if ($options['busyPeriods'] ?? false) {
            $busyPeriods = $options['busyPeriods'];
            unset($options['busyPeriods']);
            self::clearCityBusyPeriods($cityId);
            self::addBatchCityBusyPeriods($cityId, $busyPeriods);
        }
        if ($data) {
            if ($this->citySettingsM->citySettingsExists($cityId)) {
                $upd = $this->citySettingsM->updCitySettings($cityId, $data);
                if (!$upd) {
                    DB::rollback();

                    return false;
                }
            } else {
                $data['city_id'] = $cityId;
                $add             = $this->citySettingsM->addCitySettings($data);
                if (!$add) {
                    DB::rollback();

                    return false;
                }
            }
        }
        DB::commit();

        return true;
    }

    public function getCitySettingValue(int $cityId, string $settingName = null)
    {
        $setting = (array) $this->citySettingsM->getSettings($cityId);
        if (!$setting) {
            return [];
        }
        if ($settingName) {
            return $setting[$settingName] ?? null;
        }

        return $setting;
    }

    /**
     * @return array|mixed|null
     */
    public function getCitySettingValueWithDefault(int $cityId, string $settingName = null)
    {
        $citySetting = CitySettingsModel::where('city_id', $cityId)
            ->where('enabled', 1)
            ->first();

        if ($citySetting === null) {
            $citySetting = CitySettingsModel::where('city_id', 0)->first();
        }

        if ($citySetting === null) {
            return null;
        }

        if ($settingName) {
            return $citySetting->{$settingName} ?? null;
        }

        return $citySetting->toArray();
    }

    /**
     * @return bool
     */
    public function weekDayAvailable(int $cityId, int $timestamp)
    {
        $weekDay           = date('w', $timestamp);
        $weekDay           = $weekDay ?: 7;
        $serviceWeekDays   = $this->getCitySettingValueWithDefault($cityId, 'service_weekdays');
        $serviceWeekDayArr = Tool::weekdaysBin2Dec($serviceWeekDays);
        if (in_array($weekDay, $serviceWeekDayArr)) {
            return true;
        }

        return false;
    }

    /**
     * @param int $onlyStart
     *
     * @return array
     */
    public function buildCityTimePeriods(int $cityId = 0, $onlyStart = false)
    {
        $timePeriods = [];
        $setting     = $this->getCitySettingValueWithDefault($cityId);
        if (!$setting) {  // 返回默认值
            return OrderInc::TIMEPERIOD_MAP;
        }

        $hourStart = $setting['service_hour_start'];
        $hourEnd   = $setting['service_hour_end'];
        $interval  = $setting['service_interval'];

        for ($i = $hourStart; $i <= $hourEnd; $i += $interval) {
            if ($i + $interval > 24) {
                break;
            }
            $periodCode               = Tool::makePeriodCode((float)$i, (float)$interval);
            $timePeriods[$periodCode] = Tool::periodCode2StrNew((string)$periodCode, $onlyStart);
        }

        return $timePeriods;
    }

    /**
     * @param $cityId
     * @param $reservTimestamp
     *
     * @return array
     */
    public function filterReservHours($cityId, array $timePeriods, $reservTimestamp)
    {
        $curTime         = time();
        $reservDate      = date('Y-m-d', $reservTimestamp);
        $reservationHour = $this->getCitySettingValueWithDefault($cityId, 'reservation_hour');
        foreach ($timePeriods as $periodCode => $p) {
            [$periodStartHour, $periodStartMinute] = Tool::periodAnalyse((string)$periodCode);
            $periodStartTimestamp                  = strtotime($reservDate . ' ' . $periodStartHour . ':' . $periodStartMinute);
            if ($periodStartTimestamp < $curTime || $periodStartTimestamp < $curTime + $reservationHour * 3600) {
                unset($timePeriods[$periodCode]);
            }
        }

        return $timePeriods;
    }

    public static function antCityOwnSettings(array $areas): array
    {
        foreach ($areas as $key => $area) {
            $settings = (new CitySettingsModel())->getSettings($area['key']);
            if ($settings) {
                $areas[$key]['own_setting'] = 1;
            } else {
                $areas[$key]['own_setting'] = 0;
            }
        }

        return $areas;
    }

    public static function filterBusyPeriods($cityId, array $timePeriods)
    {
        $busyPeriods = (new CityBusyPeriodModel())->getCityBusyPeriods($cityId);
        if ($busyPeriods) {
            foreach ($timePeriods as $key => $period) {
                $inBusy = 0;
                foreach ($busyPeriods as $busy) {
                    $stime = $busy['start_time'];
                    $etime = $busy['end_time'];
                    if ($period >= $stime && $period < $etime) {
                        $inBusy++;
                    } else {
                        $inBusy = max(0, $inBusy--);
                    }
                }
                if ($inBusy) {
                    unset($timePeriods[$key]);
                }
            }
        }

        return $timePeriods;
    }

    public static function getBusyPeriods($cityId = 0, $tryDefault = false)
    {
        $data = (new CityBusyPeriodModel())->getCityBusyPeriods($cityId);
        if (!$data && $tryDefault) {
            $data = (new CityBusyPeriodModel())->getDefaultCityBusyPeriod();
        }

        return $data;
    }

    public static function clearCityBusyPeriods($cityId)
    {
        return (new CityBusyPeriodModel())->clearCityBusyPeriods($cityId);
    }

    public static function addBatchCityBusyPeriods(int $cityId, array $busyPeriods)
    {
        $entityList = [];
        foreach ($busyPeriods as $period) {
            $entity = new BusyPeriodEntity(new CityBusyPeriodModel());
            $entity->setCity($cityId);
            $entity->setStartTime($period['startTime'] ?? '');
            $entity->setEndTime($period['endTime'] ?? '');
            $entityList[] = $entity;
        }

        return (new CityBusyPeriodModel())->addBatchCityBusyPeriods($entityList);
    }

    public function saveBusyPeriods(array $busyPeriods, int $cityId = 0)
    {
        DB::beginTransaction();
        $data = [];
        foreach (self::$colsMap2 as $configName => $columnName) {
            if (!isset($busyPeriods[$configName])) {
                continue;
            }
            $optionValue = $busyPeriods[$configName];
            if (in_array($configName, ['serviceHourStart', 'serviceHourEnd'])) {//小时字符串转浮点数
                $optionValue = $this->citySettingsM->hour2float($optionValue);
            }
            if ($configName == 'serviceWeekdays' && $optionValue) {//星期转为数字
                $weekDayArr = array_map(function ($item) {
                    return intval(trim($item));
                }, (array) explode(',', $optionValue));
                $optionValue = Tool::weekdaysDec2Bin($weekDayArr);
            }
            if (in_array($configName, ['serviceInterval', 'reservationHour'])) {//判断0.5倍数
                if ((($optionValue * 10) % 5) != 0) {
                    continue;
                }
            }
            $data[$columnName] = $optionValue;
        }
        if ($data) {
            if ($this->citySettingsM->citySettingsExists($cityId)) {
                $upd = $this->citySettingsM->updCitySettings($cityId, $data);
                if (!$upd) {
                    DB::rollback();

                    return false;
                }
            } else {
                $data['city_id'] = $cityId;
                $add             = $this->citySettingsM->addCitySettings($data);
                if (!$add) {
                    DB::rollback();

                    return false;
                }
            }
        }
        if ($busyPeriods['busyPeriods'] ?? false) {
            self::clearCityBusyPeriods($cityId);
            $add = self::addBatchCityBusyPeriods($cityId, $busyPeriods['busyPeriods']);
            if (!$add) {
                DB::rollback();

                return false;
            }
        }
        DB::commit();

        return true;
    }
}
